import { _decorator, Component, Node, Prefab, instantiate, SpriteFrame, director, Label } from 'cc'
import { ApiMsgEnum, EntityTypeEnum, IClientInput, IMsgClientSync, IMsgServerSync, InputTypeEnum } from '../Common'
import { ActorManager } from '../Entity/Actor/ActorManager'
import { BulletManager } from '../Entity/Bullet/BulletManager'
import { EventEnum, PrefabPathEnum, SceneEnum, TexturePathEnum } from '../Enum'
import DataManager from '../Global/DataManager'
import EventManager from '../Global/EventManager'
import { NetworkManager } from '../Global/NetworkManager'
import { ObjectPoolManager } from '../Global/ObjectPoolManager'
import { ResourceManager } from '../Global/ResourceManager'
import { JoyStickManager } from '../UI/JoyStickManager'
import { deepClone } from '../Utils'
const { ccclass } = _decorator

// 战斗场景
@ccclass('BattleManager')
export class BattleManager extends Component {
  /*-------------------------变量声明-------------------------*/
  // 组件
  private stage: Node
  private ui: Node
  private result: Node

  private shouldUpdate = false
  private pendingMsg: IMsgClientSync[] = [] // 预测输入
  private tickNum = 0
  private gameOver = false

  /*-------------------------生命周期-------------------------*/
  async start() {
    this.clearGame()
    // 网络连接&&资源加载
    await Promise.all([this.connectServer(), this.loadRes()])
    this.initGame()
  }

  initGame() {
    DataManager.Instance.jm = this.ui.getComponentInChildren(JoyStickManager)
    this.initMap()
    this.shouldUpdate = true

    EventManager.Instance.on(EventEnum.ClientSync, this.handleClientSync, this)
    NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgServerSync, this.handleServerSync, this)
  }
  clearGame() {
    // 事件解绑
    EventManager.Instance.off(EventEnum.ClientSync, this.handleClientSync, this)
    NetworkManager.Instance.unListenMsg(ApiMsgEnum.MsgServerSync, this.handleServerSync, this)

    // 数据
    this.shouldUpdate = false
    this.tickNum = 0
    this.gameOver = false
    ObjectPoolManager.Instance.reset()
    DataManager.Instance.reset()

    // 节点
    this.result = this.node.getChildByName('Result')
    this.result.active = false
    this.stage = DataManager.Instance.stage = this.node.getChildByName('Stage')
    // 摇杆
    this.ui = this.node.getChildByName('UI')
    this.stage.destroyAllChildren()
  }

  update(dt) {
    if (!this.shouldUpdate) return
    this.render()
    this.tick(dt)
  }
  /*-------------------------响应函数-------------------------*/
  // 帧同步
  tick(dt) {
    this.tickActor(dt)
  }
  // 角色帧同步
  tickActor(dt) {
    this.tickNum++
    if (this.tickNum % (60 * 3) == 0 && !this.gameOver && DataManager.Instance.deadNum >= DataManager.Instance.actorMap.size - 1) {
      this.gameOver = true
      let res = null
      if (DataManager.Instance.deadNum == DataManager.Instance.actorMap.size) {
        res = '本局为平局'
      }
      DataManager.Instance.deadNum = 0
      const winner = Array.from(DataManager.Instance.actorMap.values()).find(vo => !vo.isDead)
      if (!res) res = '胜者为：' + winner.nickname

      const label = this.result.getComponentInChildren(Label)
      label.string = res

      this.result.active = true
      setTimeout(() => {
        this.leaveGame()
      }, 3500)
    }
    for (const data of DataManager.Instance.state.actors) {
      const { id } = data
      let am = DataManager.Instance.actorMap.get(id)
      if (!am) return
      am.tick(dt)
    }
  }
  // 渲染
  render() {
    // 角色
    this.renderActor()
    // 子弹
    this.renderBullet()
  }
  // 资源预加载
  async loadRes() {
    const list = []
    // 模型预加载
    for (const type in PrefabPathEnum) {
      const p = ResourceManager.Instance.loadRes(PrefabPathEnum[type], Prefab).then(prefab => {
        DataManager.Instance.prefabMap.set(type, prefab)
      })
      list.push(p)
    }
    // 纹理预加载
    for (const type in TexturePathEnum) {
      const p = ResourceManager.Instance.loadDir(TexturePathEnum[type], SpriteFrame).then(spriteFrames => {
        DataManager.Instance.textureMap.set(type, spriteFrames)
      })
      list.push(p)
    }

    await Promise.all(list)
  }
  // 初始化地图
  initMap() {
    const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.Map)
    const map = instantiate(prefab)
    map.setParent(this.stage)
  }
  // 角色渲染
  async renderActor() {
    // 角色存在则更新否则初始化
    for (const data of DataManager.Instance.state.actors) {
      const { id, type } = data
      let am = DataManager.Instance.actorMap.get(id)
      if (!am) {
        // 获取角色模型并实例化
        const prefab = DataManager.Instance.prefabMap.get(type)
        const actor = instantiate(prefab)
        // 放入舞台
        actor.setParent(this.stage)
        // 添加控制模块
        am = actor.addComponent(ActorManager)
        // 数据中添加角色信息
        DataManager.Instance.actorMap.set(id, am)
        // 角色初始化
        am.init(data)
      } else {
        // 更新角色
        am.render(data)
      }
    }
  }
  // 子弹渲染
  async renderBullet() {
    // 角色存在则更新否则初始化
    for (const data of DataManager.Instance.state.bullets) {
      const { id, type } = data
      let bm = DataManager.Instance.bulletMap.get(id)
      if (!bm) {
        // 获取子弹模型并实例化
        const bullet = ObjectPoolManager.Instance.get(type)
        // 添加控制模块
        bm = bullet.getComponent(BulletManager) || bullet.addComponent(BulletManager)
        // 数据库中添加子弹
        DataManager.Instance.bulletMap.set(id, bm)
        // 子弹初始化
        bm.init(data)
      } else {
        // 更新子弹
        bm.render(data)
      }
    }
  }

  // 连接服务端
  async connectServer() {
    if (!(await NetworkManager.Instance.connect().catch(() => false))) {
      await this.connectServer()
    }
  }
  // 客户端-->服务端帧同步
  handleClientSync(input: IClientInput) {
    const msg = {
      input,
      frameID: DataManager.Instance.frameID++,
    }
    NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, msg)
    // 先行预测
    if (input.type === InputTypeEnum.ActorMove) {
      DataManager.Instance.applyInput(input)
      this.pendingMsg.push(msg)
    }
  }
  // 服务端-->客户端帧同步
  handleServerSync({ inputs, lastFrameID }: IMsgServerSync) {
    // 回滚数据
    DataManager.Instance.state = DataManager.Instance.lastState
    // 应用服务端输入
    for (const input of inputs) {
      DataManager.Instance.applyInput(input)
    }
    // 记录最新服务数据
    DataManager.Instance.lastState = deepClone(DataManager.Instance.state)
    // 比较服务端与本地操作并同步
    if (!this.pendingMsg) this.pendingMsg = []
    this.pendingMsg = this.pendingMsg.filter(msg => msg.frameID > lastFrameID)
    if (this.pendingMsg.length == 0) return
    for (const msg of this.pendingMsg) {
      DataManager.Instance.applyInput(msg.input)
    }
  }

  // 离开游戏
  async leaveGame() {
    const { success, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomLeave, {})
    if (!success) return console.log('api错误：', error)
    DataManager.Instance.roomInfo = null
    this.clearGame()
    director.loadScene(SceneEnum.Hall)
  }
}
